package cloud.take.off.utils;

import com.baomidou.mybatisplus.core.toolkit.StringPool;
import org.springframework.util.Assert;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalAmount;
import java.time.temporal.TemporalQuery;
import java.util.*;

/**
 * 日期工具类
 *
 * @author L.cm
 */
public class DateUtil {

	public static final String PATTERN_DATETIME = "yyyy-MM-dd HH:mm:ss";
	public static final String PATTERN_YEAR = "yyyy";
	public static final String PATTERN_MONTH = "yyyy-MM";
	public static final String PATTERN_DATE = "yyyy-MM-dd";
	public static final String PATTERN_SLASH_DATE = "yyyy/MM/dd";
	public static final String PATTERN_TIME = "HH:mm:ss";
	public static final String PATTERN_ONLY_MONTH = "MM";
	public static final String MIN_HOUR_MINUTE_SECOND = " 00:00:00";
	public static final String MAX_HOUR_MINUTE_SECOND = " 23:59:59";
	/**
	 * 老 date 格式化
	 */
	public static final ConcurrentDateFormat DATETIME_FORMAT = ConcurrentDateFormat.of(PATTERN_DATETIME);
	public static final ConcurrentDateFormat DATE_FORMAT = ConcurrentDateFormat.of(PATTERN_DATE);
	public static final ConcurrentDateFormat TIME_FORMAT = ConcurrentDateFormat.of(PATTERN_TIME);
	/**
	 * java 8 时间格式化
	 */
	public static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern(DateUtil.PATTERN_DATETIME);
	public static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern(DateUtil.PATTERN_DATE);
	public static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern(DateUtil.PATTERN_TIME);

    /**
	 * 获取当前日期
	 * @return 当前日期
	 */
	public static Date now() {
		return new Date();
	}

	/**
	 * 添加年
	 *
	 * @param date       时间
	 * @param yearsToAdd 添加的年数
	 * @return 设置后的时间
	 */
	public static Date plusYears(Date date, int yearsToAdd) {
		return DateUtil.set(date, Calendar.YEAR, yearsToAdd);
	}

	/**
	 * 添加月
	 *
	 * @param date        时间
	 * @param monthsToAdd 添加的月数
	 * @return 设置后的时间
	 */
	public static Date plusMonths(Date date, int monthsToAdd) {
		return DateUtil.set(date, Calendar.MONTH, monthsToAdd);
	}

	/**
	 * 添加周
	 *
	 * @param date       时间
	 * @param weeksToAdd 添加的周数
	 * @return 设置后的时间
	 */
	public static Date plusWeeks(Date date, int weeksToAdd) {
		return DateUtil.plus(date, Period.ofWeeks(weeksToAdd));
	}

	/**
	 * 添加天
	 *
	 * @param date      时间
	 * @param daysToAdd 添加的天数
	 * @return 设置后的时间
	 */
	public static Date plusDays(Date date, long daysToAdd) {
		return DateUtil.plus(date, Duration.ofDays(daysToAdd));
	}

	/**
	 * 添加小时
	 *
	 * @param date       时间
	 * @param hoursToAdd 添加的小时数
	 * @return 设置后的时间
	 */
	public static Date plusHours(Date date, long hoursToAdd) {
		return DateUtil.plus(date, Duration.ofHours(hoursToAdd));
	}

	/**
	 * 添加分钟
	 *
	 * @param date         时间
	 * @param minutesToAdd 添加的分钟数
	 * @return 设置后的时间
	 */
	public static Date plusMinutes(Date date, long minutesToAdd) {
		return DateUtil.plus(date, Duration.ofMinutes(minutesToAdd));
	}

	/**
	 * 添加秒
	 *
	 * @param date         时间
	 * @param secondsToAdd 添加的秒数
	 * @return 设置后的时间
	 */
	public static Date plusSeconds(Date date, long secondsToAdd) {
		return DateUtil.plus(date, Duration.ofSeconds(secondsToAdd));
	}

	/**
	 * 添加毫秒
	 *
	 * @param date        时间
	 * @param millisToAdd 添加的毫秒数
	 * @return 设置后的时间
	 */
	public static Date plusMillis(Date date, long millisToAdd) {
		return DateUtil.plus(date, Duration.ofMillis(millisToAdd));
	}

	/**
	 * 添加纳秒
	 *
	 * @param date       时间
	 * @param nanosToAdd 添加的纳秒数
	 * @return 设置后的时间
	 */
	public static Date plusNanos(Date date, long nanosToAdd) {
		return DateUtil.plus(date, Duration.ofNanos(nanosToAdd));
	}

	/**
	 * 日期添加时间量
	 *
	 * @param date   时间
	 * @param amount 时间量
	 * @return 设置后的时间
	 */
	public static Date plus(Date date, TemporalAmount amount) {
		Instant instant = date.toInstant();
		return Date.from(instant.plus(amount));
	}

	/**
	 * 减少年
	 *
	 * @param date  时间
	 * @param years 减少的年数
	 * @return 设置后的时间
	 */
	public static Date minusYears(Date date, int years) {
		return DateUtil.set(date, Calendar.YEAR, -years);
	}

	/**
	 * 减少月
	 *
	 * @param date   时间
	 * @param months 减少的月数
	 * @return 设置后的时间
	 */
	public static Date minusMonths(Date date, int months) {
		return DateUtil.set(date, Calendar.MONTH, -months);
	}

	/**
	 * 减少周
	 *
	 * @param date  时间
	 * @param weeks 减少的周数
	 * @return 设置后的时间
	 */
	public static Date minusWeeks(Date date, int weeks) {
		return DateUtil.minus(date, Period.ofWeeks(weeks));
	}

	/**
	 * 减少天
	 *
	 * @param date 时间
	 * @param days 减少的天数
	 * @return 设置后的时间
	 */
	public static Date minusDays(Date date, long days) {
		return DateUtil.minus(date, Duration.ofDays(days));
	}

	/**
	 * 减少小时
	 *
	 * @param date  时间
	 * @param hours 减少的小时数
	 * @return 设置后的时间
	 */
	public static Date minusHours(Date date, long hours) {
		return DateUtil.minus(date, Duration.ofHours(hours));
	}

	/**
	 * 减少分钟
	 *
	 * @param date    时间
	 * @param minutes 减少的分钟数
	 * @return 设置后的时间
	 */
	public static Date minusMinutes(Date date, long minutes) {
		return DateUtil.minus(date, Duration.ofMinutes(minutes));
	}

	/**
	 * 减少秒
	 *
	 * @param date    时间
	 * @param seconds 减少的秒数
	 * @return 设置后的时间
	 */
	public static Date minusSeconds(Date date, long seconds) {
		return DateUtil.minus(date, Duration.ofSeconds(seconds));
	}

	/**
	 * 减少毫秒
	 *
	 * @param date   时间
	 * @param millis 减少的毫秒数
	 * @return 设置后的时间
	 */
	public static Date minusMillis(Date date, long millis) {
		return DateUtil.minus(date, Duration.ofMillis(millis));
	}

	/**
	 * 减少纳秒
	 *
	 * @param date  时间
	 * @param nanos 减少的纳秒数
	 * @return 设置后的时间
	 */
	public static Date minusNanos(Date date, long nanos) {
		return DateUtil.minus(date, Duration.ofNanos(nanos));
	}

	/**
	 * 日期减少时间量
	 *
	 * @param date   时间
	 * @param amount 时间量
	 * @return 设置后的时间
	 */
	public static Date minus(Date date, TemporalAmount amount) {
		Instant instant = date.toInstant();
		return Date.from(instant.minus(amount));
	}

	/**
	 * 设置日期属性
	 *
	 * @param date          时间
	 * @param calendarField 更改的属性
	 * @param amount        更改数，-1表示减少
	 * @return 设置后的时间
	 */
	private static Date set(Date date, int calendarField, int amount) {
		Assert.notNull(date, "The date must not be null");
		Calendar c = Calendar.getInstance();
		c.setLenient(false);
		c.setTime(date);
		c.add(calendarField, amount);
		return c.getTime();
	}

	/**
	 * 日期时间格式化
	 *
	 * @param date 时间
	 * @return 格式化后的时间
	 */
	public static String formatDateTime(Date date) {
		return DATETIME_FORMAT.format(date);
	}

	/**
	 * 日期格式化
	 *
	 * @param date 时间
	 * @return 格式化后的时间
	 */
	public static String formatDate(Date date) {
		return DATE_FORMAT.format(date);
	}

	/**
	 * 时间格式化
	 *
	 * @param date 时间
	 * @return 格式化后的时间
	 */
	public static String formatTime(Date date) {
		return TIME_FORMAT.format(date);
	}

	/**
	 * 日期格式化
	 *
	 * @param date    时间
	 * @param pattern 表达式
	 * @return 格式化后的时间
	 */
	public static String format(Date date, String pattern) {
		return ConcurrentDateFormat.of(pattern).format(date);
	}

	/**
	 * java8 日期时间格式化
	 *
	 * @param temporal 时间
	 * @return 格式化后的时间
	 */
	public static String formatDateTime(TemporalAccessor temporal) {
		return DATETIME_FORMATTER.format(temporal);
	}

	/**
	 * java8 日期时间格式化
	 *
	 * @param temporal 时间
	 * @return 格式化后的时间
	 */
	public static String formatDate(TemporalAccessor temporal) {
		return DATE_FORMATTER.format(temporal);
	}

	/**
	 * java8 时间格式化
	 *
	 * @param temporal 时间
	 * @return 格式化后的时间
	 */
	public static String formatTime(TemporalAccessor temporal) {
		return TIME_FORMATTER.format(temporal);
	}

	/**
	 * java8 日期格式化
	 *
	 * @param temporal 时间
	 * @param pattern  表达式
	 * @return 格式化后的时间
	 */
	public static String format(TemporalAccessor temporal, String pattern) {
		return DateTimeFormatter.ofPattern(pattern).format(temporal);
	}

	/**
	 * 将字符串转换为时间
	 *
	 * @param dateStr 时间字符串
	 * @param pattern 表达式
	 * @return 时间
	 */
	public static Date parse(String dateStr, String pattern) {
		ConcurrentDateFormat format = ConcurrentDateFormat.of(pattern);
		try {
			return format.parse(dateStr);
		} catch (ParseException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 将字符串转换为时间
	 *
	 * @param dateStr 时间字符串
	 * @param format  ConcurrentDateFormat
	 * @return 时间
	 */
	public static Date parse(String dateStr, ConcurrentDateFormat format) {
		try {
			return format.parse(dateStr);
		} catch (ParseException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 将字符串转换为时间
	 *
	 * @param dateStr 时间字符串
	 * @param pattern 表达式
	 * @return 时间
	 */
	public static <T> T parse(String dateStr, String pattern, TemporalQuery<T> query) {
		return DateTimeFormatter.ofPattern(pattern).parse(dateStr, query);
	}

	/**
	 * 时间转 Instant
	 *
	 * @param dateTime 时间
	 * @return Instant
	 */
	public static Instant toInstant(LocalDateTime dateTime) {
		return dateTime.atZone(ZoneId.systemDefault()).toInstant();
	}

	/**
	 * Instant 转 时间
	 *
	 * @param instant Instant
	 * @return Instant
	 */
	public static LocalDateTime toDateTime(Instant instant) {
		return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
	}

	/**
	 * 转换成 date
	 *
	 * @param dateTime LocalDateTime
	 * @return Date
	 */
	public static Date toDate(LocalDateTime dateTime) {
		return Date.from(DateUtil.toInstant(dateTime));
	}

	/**
	 * 转换成 date
	 *
	 * @param localDate LocalDate
	 * @return Date
	 */
	public static Date toDate(final LocalDate localDate) {
		return Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
	}

	/**
	 * Converts local date time to Calendar.
	 */
	public static Calendar toCalendar(final LocalDateTime localDateTime) {
		return GregorianCalendar.from(ZonedDateTime.of(localDateTime, ZoneId.systemDefault()));
	}

	/**
	 * localDateTime 转换成毫秒数
	 *
	 * @param localDateTime LocalDateTime
	 * @return long
	 */
	public static long toMilliseconds(final LocalDateTime localDateTime) {
		return localDateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
	}

	/**
	 * localDate 转换成毫秒数
	 *
	 * @param localDate LocalDate
	 * @return long
	 */
	public static long toMilliseconds(LocalDate localDate) {
		return toMilliseconds(localDate.atStartOfDay());
	}

	/**
	 * 转换成java8 时间
	 *
	 * @param calendar 日历
	 * @return LocalDateTime
	 */
	public static LocalDateTime fromCalendar(final Calendar calendar) {
		TimeZone tz = calendar.getTimeZone();
		ZoneId zid = tz == null ? ZoneId.systemDefault() : tz.toZoneId();
		return LocalDateTime.ofInstant(calendar.toInstant(), zid);
	}

	/**
	 * 转换成java8 时间
	 *
	 * @param instant Instant
	 * @return LocalDateTime
	 */
	public static LocalDateTime fromInstant(final Instant instant) {
		return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
	}

	/**
	 * 转换成java8 时间
	 *
	 * @param date Date
	 * @return LocalDateTime
	 */
	public static LocalDateTime fromDate(final Date date) {
		return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
	}

	/**
	 * 转换成java8 时间
	 *
	 * @param milliseconds 毫秒数
	 * @return LocalDateTime
	 */
	public static LocalDateTime fromMilliseconds(final long milliseconds) {
		return LocalDateTime.ofInstant(Instant.ofEpochMilli(milliseconds), ZoneId.systemDefault());
	}

	/**
	 * 比较2个时间差，跨度比较小
	 *
	 * @param startInclusive 开始时间
	 * @param endExclusive   结束时间
	 * @return 时间间隔
	 */
	public static Duration between(Temporal startInclusive, Temporal endExclusive) {
		return Duration.between(startInclusive, endExclusive);
	}

	/**
	 * 比较2个时间差，跨度比较大，年月日为单位
	 *
	 * @param startDate 开始时间
	 * @param endDate   结束时间
	 * @return 时间间隔
	 */
	public static Period between(LocalDate startDate, LocalDate endDate) {
		return Period.between(startDate, endDate);
	}

	/**
	 * 比较2个 时间差
	 *
	 * @param startDate 开始时间
	 * @param endDate   结束时间
	 * @return 时间间隔
	 */
	public static Duration between(Date startDate, Date endDate) {
		return Duration.between(startDate.toInstant(), endDate.toInstant());
	}

	/**
	 * 获取今天的日期
	 *
	 * @return 时间
	 */
	public static String today() {
		return format(new Date(), "yyyyMMdd");
	}

	public static boolean isTheSameYear(Date d1, Date d2) {
		Calendar c1 = Calendar.getInstance();
		Calendar c2 = Calendar.getInstance();
		c1.setTime(d1);
		c2.setTime(d2);
		return (c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR));
	}

	public static boolean isTheSameDay(Date d1, Date d2) {
		Calendar c1 = Calendar.getInstance();
		Calendar c2 = Calendar.getInstance();
		c1.setTime(d1);
		c2.setTime(d2);
		return (c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR))
				&& (c1.get(Calendar.MONTH) == c2.get(Calendar.MONTH))
				&& (c1.get(Calendar.DAY_OF_MONTH) == c2.get(Calendar.DAY_OF_MONTH));
	}

	/**
	 * 获取当月的最后一天
	 * @param date
	 * @return
	 */
	public static String  getThisMonthlastdate(String date){
		Date d=parse(date,PATTERN_MONTH);
		d=minusDays(plusMonths(d,1),1);
		String str=format(d,PATTERN_DATE);
		return str;
	}

	/**
	 * 获取当月的最后一天
	 * @param date
	 * @return
	 */
	public static Date getThisMonthLastDate(String date){
		Date d = null;
		try {
			d = parse(date, PATTERN_MONTH);
			d = minusDays(plusMonths(d,1),1);
		}catch (Exception e){
			e.printStackTrace();
		}finally {
			return d;
		}
	}

	/**
	 * 获取上个月月份
	 * @param date
	 * @return
	 */
	public static String getLastMonth(String date){
		Date d=parse(date,PATTERN_MONTH);
		d=minusMonths(d,1);
		String str=format(d,PATTERN_MONTH);
		return  str;
	}

	/**
	 * 获取上月的最后一天
	 * @param date
	 * @return
	 */
	public static String  getLastMonthlastdate(String date){
		Date d=parse(date,PATTERN_MONTH);
		d=minusDays(d,1);
		String str=format(d,PATTERN_DATE);
		return str;
	}

	/**
	 * 	根据周期性切分时间
	 * @param periodic 周期性
	 * @param dBegin 开始时间
	 * @param dEnd 结束时间
	 * @return
	 */
	public static List<String> findDates(Integer periodic, Date dBegin, Date dEnd){
		List<String> listDate = new ArrayList<>();
		Calendar calBegin = Calendar.getInstance();
		calBegin.setTime(dBegin);
		Calendar calEnd = Calendar.getInstance();
		calEnd.setTime(dEnd);
		//添加初始日期
		listDate.add(new SimpleDateFormat("yyyy-MM").format(calBegin.getTime()));
		while (calEnd.after(calBegin)) {
			switch (periodic) {
				case 1:
					calBegin.add(Calendar.MONTH, 1);break;
				case 2:
					calBegin.add(Calendar.MONTH, 3);break;
				case 3:
					calBegin.add(Calendar.MONTH, 6);break;
				case 4:
					calBegin.add(Calendar.YEAR, 1);break;
				default:
					break;
			}
			if (calEnd.after(calBegin) || calEnd.equals(calBegin)){
				listDate.add(new SimpleDateFormat("yyyy-MM").format(calBegin.getTime()));
			}

		}
		return listDate;
	}

	/**
	 * 	根据周期性切分时间
	 * @param periodic 周期性
	 * @param dBegin 开始时间
	 * @param dEnd 结束时间
	 * @return
	 */
	public static List<String> findDatesByDay(Integer periodic, Date dBegin, Date dEnd){
		List<String> listDate = new ArrayList<>();
		Calendar calBegin = Calendar.getInstance();
		calBegin.setTime(dBegin);
		Calendar calEnd = Calendar.getInstance();
		calEnd.setTime(dEnd);
		//添加初始日期
		listDate.add(getThisMonthlastdate(new SimpleDateFormat(PATTERN_DATE).format(calBegin.getTime())));
		while (calEnd.after(calBegin)) {
			switch (periodic) {
				case 1:
					calBegin.add(Calendar.MONTH, 1);break;
				case 2:
					calBegin.add(Calendar.MONTH, 3);break;
				case 3:
					calBegin.add(Calendar.MONTH, 6);break;
				case 4:
					calBegin.add(Calendar.YEAR, 1);break;
				case 5:
					calBegin.add(Calendar.DATE, 1);break;
				default:
					break;
			}
			if (calEnd.after(calBegin) || calEnd.equals(calBegin)){
				String thisMonthlastdate = getThisMonthlastdate(new SimpleDateFormat(PATTERN_DATE).format(calBegin.getTime()));
				if (parse(thisMonthlastdate,PATTERN_DATE).before(calEnd.getTime())){
					listDate.add(thisMonthlastdate);
				}
			}
		}
		listDate.add(new SimpleDateFormat(PATTERN_DATE).format(calEnd.getTime()));
		return listDate;
	}

	public static List<String> findBetweenDates(Integer periodic, Date dBegin, Date dEnd){
		List<String> listDate = new ArrayList<>();
		Calendar calBegin = Calendar.getInstance();
		calBegin.setTime(dBegin);
		Calendar calEnd = Calendar.getInstance();
		calEnd.setTime(dEnd);
		//添加初始日期
		listDate.add(new SimpleDateFormat(PATTERN_DATE).format(calBegin.getTime()));
		while (calEnd.after(calBegin)) {
			switch (periodic) {
				case 1:
					calBegin.add(Calendar.MONTH, 1);break;
				case 2:
					calBegin.add(Calendar.MONTH, 3);break;
				case 3:
					calBegin.add(Calendar.MONTH, 6);break;
				case 4:
					calBegin.add(Calendar.YEAR, 1);break;
				default:
					break;
			}
			if (calEnd.after(calBegin) || calEnd.equals(calBegin)){
				listDate.add(new SimpleDateFormat(PATTERN_DATE).format(calBegin.getTime()));
			}

		}
		return listDate;
	}
	/**
	 * @Description: //TODO 月份 List 转为最后一天
	 * @Author: Take-off
	 * @Date: 9:51 AM 2024/3/6
	 * @Param: [months]
	 * @return: java.util.List<java.lang.String>
	 **/
	public static List<String> monthToLastDay(List<String> months){
		List<String> dates = new ArrayList<>();
		for (String month:months){
			Date lastDay = lastDay(month);
			dates.add(format(lastDay, PATTERN_DATE));
		}
		return dates;
	}

	public static Date lastDay(String month){
		//查询最后一天
		Date lastDay = minusDays(plusMonths(parse(month, PATTERN_MONTH), 1), 1);
		if (format(now(),PATTERN_MONTH).equals(month)){
			//查询前一天
			lastDay = minusDays(now(),1);
		}
		return lastDay;
	}

	public static boolean isTheSameMonth(String date){
		String nowdate=format(new Date(),PATTERN_MONTH);
		if(date.equals(nowdate)){
			return true;
		}else{
			return  false;
		}
	}

	/**
	 * @Description: //TODO 返回当前年份多少天
	 * @Author: Take-off
	 * @Date: 4:08 PM 2023/8/22
	 * @Param: [date, type]
	 * @return: java.lang.Integer
	 **/
	public static Integer daysForYear(Date date,Integer type){
		int years = 360;
		if (Optional.ofNullable(type).isPresent() && type == 1){
			years = 365;
		}
		return years;
	}

	/**
	 * @Description:  两个时间隔几天
	 * @Author: Take-off
	 * @Date: 2:36 PM 2023/8/22
	 * @Param: [startDate, endDate]
	 * @return: java.lang.Integer
	 **/
	public static Integer daysBetween(Date startDate,Date endDate,Integer type){
//		if (Optional.ofNullable(type).isPresent() && type == 1){
//			long startTime = startDate.getTime();
//			long endTime = endDate.getTime();
//			return (int) ((endTime - startTime) / (1000 * 60 * 60 * 24));
//		}else {
//			Temporal temporal1 = LocalDate.parse(format(startDate,PATTERN_DATE));
//			Temporal temporal2 = LocalDate.parse(format(endDate,PATTERN_DATE));
//			// 方法返回为相差月份
//			long l = ChronoUnit.MONTHS.between(temporal1, temporal2);
//			return (int)l*30;
//		}
			long startTime = startDate.getTime();
			long endTime = endDate.getTime();
			return (int) ((endTime - startTime) / (1000 * 60 * 60 * 24)) + 1;
	}
}
